const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const webpack = require('webpack');
const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin');
const {
  CleanWebpackPlugin
} = require('clean-webpack-plugin');
const myPlugin = require('./webpackPlugin/myWebpackPlugin');

const MiniCssExtractPlugin = require('mini-css-extract-plugin');

const FileManagerPlugin = require('filemanager-webpack-plugin');

const TerserJSplugin = require('terser-webpack-plugin')

const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin')

const ModuleConcatenationPlugin = require('webpack/lib/optimize/ModuleConcatenationPlugin')

// const Happypack = require('happypack')

// const WbpackParallelUglifyPlugin = require('webpack-parallel-uglify-plugin')

// const WorkboxPlugin = require('workbox-webpack-plugin');

const {
  TsConfigPathsPlugin
} = require('awesome-typescript-loader')

const outputPath = './.app';

const packageName = require('./package.json').name;

const dllPath = `./dll`;

module.exports = {
  // mode: 'production', // 'production' | 'development'
  // devtool: 'hidden-sourse-map', // sourse相关
  resolveLoader: {
    // 默认的loader路径是node_module在这里可以添加新的路径
    modules: ['node_modules', path.resolve(__dirname, './loader_module')],
    extensions: ['.js', '.json'],
    mainFields: ['jsnext:main', 'browser', 'main'] // 优先使用第三方模块中jsnext:main中指向的es6代码的文件（scope hosting相关）
  },
  performance: {
    hints: false, // 如果一个资源超过250kb，webpack会进行输出一个警告来通知你
  },
  entry: {
    index: path.resolve(__dirname, './src/index'),
    // page1: path.resolve(__dirname, './src/page1'),
  },
  output: {
    // 微前端
    // library: `${packageName}-[name]`,
    // libraryTarget: 'umd',
    // jsonpFunction: `webpackJsonp_${packageName}`,

    publicPath: '', //静态文件前缀（https://a.com）
    path: path.resolve(__dirname, `${outputPath}/`),
    filename: '[name].[contenthash].js',
    chunkFilename: '[name].[contenthash].js',
    // filename: '[name].[hash].js',
    // hash依赖文件同一个hash(项目修改文件都会修改),
    // contenthash一个文件对应一个名字(文件修改才会修改), css用
    // chunkhash文件会共用chunkhash,内容没有更新就不会重新构建(依赖更新了也会改变), js用
    // 配置这个不然热更新会找不到内存中对应的文件(多页面也有相关的配置)
    // 也有利于进行资源持久化缓存(webpack会通过文件内容去计算hash值,文件没有改变的话下次打包hash值不会变,便于浏览器缓存)
  },
  optimization: {
    // 优化一般在生产环境才进行这项配置
    usedExports: true, // 告知 webpack 去决定每个模块使用的导出内容配合tree sharking
    splitChunks: {
      // 分块策略
      chunks: 'all',
      // all 全部chunk都处理 initial对异步的文件不处理 async只对异步文件处理
      cacheGroups: {
        vender: {
          // vender一般表示第三方的库只要有用到1次
          name: 'vender',
          test: /node_modules/, // 路径
          minSize: 0, // 大小超过0字节就分包
          minChunks: 1, // 最少引用1次
          priority: 1, // 优先级
          chunks: 'initial',
        },
        common: {
          // 公共文件 一般是多次引用的时候
          name: 'common',
          test: /src/,
          chunks: 'all',
          minSize: 0,
          minChunks: 2,
        },
      },
    },
    runtimeChunk: { // 提取runtime部分的代码到单独的文件中
      name: 'runtime'
    },
    // 压缩css
    minimizer: [
      new TerserJSplugin({}),
      new OptimizeCssAssetsPlugin({})
    ]
  },
  resolve: {
    extensions: ['.js', '.tsx', '.ts'],
    alias: {
      '@': path.resolve(__dirname, './src'), // 如果是ts检查需要定义tsconfig.json的paths
    },
    // plugins: [
    //   new TsConfigPathsPlugin()
    // ],
  },
  externals: {
    // 防止打包时捆绑某些imported软件包，而是在运行时检索这些外部依赖项。在html模板文件中引入要进行cdn的依赖包。但是开发时依然要import那些包
    // dll不用设置这个
    jquery: 'jQuery',
    // 'react': 'React',
    // 'react-dom': 'ReactDOM'
  },
  module: {
    noParse: [/jquery/], // 对没有模块化的库进行忽略递归处理
    rules: [
      // {
      //   test: /.(js|tsx|ts)$/,
      //   include: [path.resolve(__dirname, './src')],
      //   loader: 'babel-loader'
      // },
      {
        test: /\.(tsx|ts)$/,
        include: [path.resolve(__dirname, './src')],
        use: [
          //顺序要对(从下往上执行和plugin相反)
          'thread-loader', // 生产环境使用
          // 'babel-loader?cacheDirectory', // 开启babel缓存 // 默认的缓存地址是node_modules/.cache下的babel-loader
          {
            loader: 'ts-loader',
            options: {
              happyPackMode: true, // 要使用thread-loader需要开启不然会产生冲突
              allowTsInNodeModules: true, // 编译node_modules中的ts文件
            },
          },
        ],
        // loader: 'awesome-typescript-loader',
      },
      {
        test: /\.(scss)$/,
        use: [
          //顺序要对
          MiniCssExtractPlugin.loader, // 打包的时候再用
          // 'thread-loader', // 生产环境使用 并且放在MiniCssExtractPlugin.loader后面
          // 'style-loader', // 开发阶段用这个
          'css-loader',
          'sass-loader',
        ],
      },
      {
        test: /\.(css)$/,
        use: [
          //顺序要对
          MiniCssExtractPlugin.loader, // 打包的时候再用
          // 'thread-loader', // 生产环境使用 并且放在MiniCssExtractPlugin.loader后面
          // 'style-loader', // 开发阶段用这个
          'css-loader',
        ],
      },
      {
        test: /\.(less)$/,
        use: [
          MiniCssExtractPlugin.loader, // 打包的时候再用
          // 'thread-loader', // 生产环境使用 并且放在MiniCssExtractPlugin.loader后面
          // 'style-loader', // 开发阶段用这个
          {
            loader: 'css-loader'
          },
          {
            loader: 'less-loader',
            options: {
              // modifyVars: { // 需要定义这边的不然页面上的less文件会报@primary-color undefined
              //   '@primary-color': 'blue',
              // },
              // async: false,
              // env: 'development',
              // javascriptEnabled: true, // 允许less使用js封装
            }
          }
        ],
      },
      // { // 放到webpack merge中测试
      //   test: /\.(js|vue|tsx|css|ts)/,
      //   loader: 'webpack-loader',
      //   include: [path.resolve(__dirname, './src')],
      //   // exclude: ['node_modules'],
      //   options: {
      //     textString: '测试的loader字符串被我换了',
      //   },
      // },
    ],
  },
  devServer: {
    hot: true, //开启热更替
    host: 'localhost',
    // overlay: true, // 出错时显示全屏
    overlay: {
      // 出现编译器错误或警告时，在浏览器中显示全屏覆盖
      warnings: true,
      errors: true,
    },
    compress: false, //对服务器使用gzip压缩
    port: 9001, //自定义端口
  },
  plugins: [ // (按顺序执行和loader相反)
    new ModuleConcatenationPlugin(), // 开启scope hosting合并每个文件生成的函数成一个函数并减少生成作用域
    new MiniCssExtractPlugin({
      filename: '[name].[contenthash].css',
    }),
    new myPlugin({ // 放到webpack merge中测试
      name: '2345678op',
      str: 2134567890,
    }),
    new CleanWebpackPlugin(), //清除上次打包的文件,和dll动态链接库配合使用要将dll生成到非输出路径
    new webpack.HotModuleReplacementPlugin(), //配置hmr热更替
    new HtmlWebpackPlugin({
      filename: 'index.html',
      ASSETSPUBLICPATH: "testpath/",
      // excludeChunks: ['page1'],
      chunks: ['index', 'vender', 'common'], // 入口文件引入，注意分包策略
      template: path.resolve(__dirname, './public/index.html'),
    }),
    // new HtmlWebpackPlugin({
    //   filename: 'page1.html',
    //   excludeChunks: ['index'], // 多页面配置这个可以避免渲染不同页面的js不会再页面文件中加入编译后的js(在output中有配置)
    //   template: path.resolve(__dirname, './public/page1.html'),
    // }),
    new CopyWebpackPlugin({
      //使引入的静态文件可以起作用
      patterns: [{
        from: path.resolve(__dirname, './public'),
        to: path.resolve(__dirname, outputPath),
      }, ],
    }),
    // dll
    new webpack.DllReferencePlugin({ // 开发环境使用
      context: __dirname,
      manifest: require(path.resolve(__dirname, `${dllPath}/manifest.json`))
    }),
    new webpack.DefinePlugin({
      // 全局变量
      WEB_MODE: false,
      'process.env': {
        a: 2345678,
      },
      DEBUG: true // 配合学习babel
    }),
    // new FileManagerPlugin({
    //   events: {
    //     onEnd: {
    //       archive: [{
    //         source: './app',
    //         destination: 'front.zip'
    //       }, ]
    //     }
    //   }
    // }),
    // dll
    new AddAssetHtmlPlugin([{
      filepath: `${dllPath}/vendor.js`, //要添加到编译中的文件的绝对路径
      outputPath: `dll/`,
      publicPath: `dll/`,
    }, ]),
    // new WorkboxPlugin.GenerateSW({ // 开启service workers
    //   // these options encourage the ServiceWorkers to get in there fast
    //   // and not allow any straggling "old" SWs to hang around
    //   clientsClaim: true,
    //   skipWaiting: true,
    // }),
    // new Happypack({ // 多进程打包一般生产用（这边配置的是babel打包的多进程）
    //   id: 'babel', // 唯一标识符，Happypack是用来处理某一类特定文件的
    //   loaders: ['babel-loader?cacheDirectory'] // 如何处理js文件和loader配置一样加上?cacheDirectory是开启缓存的配置
    // }),
    // new WbpackParallelUglifyPlugin({ // 多进程压缩js一般生产用
    //   uglifyJS: {
    //     output: {
    //       beautify: false, // 最近凑输出
    //       comments: false // 删除所有注释
    //     },
    //     compress: {
    //       drop_console: true, // 删除console
    //       collapse_vars: true, // 内嵌定义但是只使用一次的变量
    //       reduce_vars: true // 将出现多次但是没有定义成变量的静态值定义成变量
    //     }
    //   }
    // })
  ],
};
